package com.ibm.cps.parsers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Preconditions;
import com.google.gson.JsonParser;
import com.ibm.util.ErrorCode;
import com.ibm.util.JSONUtility;
import com.ibm.util.exception.CPSException;

public class JsonMessageParser extends MessageParser<ObjectNode> {

	private static MessageParser messageParser = null;
	private JsonParser parser;

	public static MessageParser getMessageParser() {
		if (messageParser == null) {
			synchronized (MessageParser.class) {
				if (messageParser == null) {
					messageParser = new JsonMessageParser();
				}
			}
		}
		return messageParser;
	}

	public JsonMessageParser() {
		parser = new JsonParser();
	}

	@Override
	public HashMap<String, String> getKeyValue(String input)
			throws CPSException {
		HashMap<String, String> key2Value = new HashMap<String, String>();
		ObjectNode objectNode = JSONUtility.fromObjectString(input);
		Iterator<String> ite = objectNode.fieldNames();
		String fieldName;
		while (ite.hasNext()) {
			fieldName = ite.next();
			if (objectNode.get(fieldName) instanceof ArrayNode) {
				key2Value.put(fieldName, objectNode.get(fieldName).toString());
			} else {
				key2Value.put(fieldName, objectNode.get(fieldName).asText());
			}
		}
		if (key2Value.size() == 0) {
			return null;
		}
		return key2Value;
	}

	@Override
	public Collection<ObjectNode> parse(String input) throws CPSException {
		// System.out.println("Input message is " + input);
		JsonNode output = JSONUtility.fromString(input);
		Collection<ObjectNode> results = new ArrayList<ObjectNode>();
		if (output.isArray()) {
			ArrayNode array = (ArrayNode) output;
			Iterator<JsonNode> ite = array.iterator();
			while (ite.hasNext()) {
				results.add((ObjectNode) (ite.next()));
			}
		} else if (output.isObject()) {
			results.add((ObjectNode) output);
		} else {
			throw new CPSException(ErrorCode.INPUT_ERROR, "Error input format.");
		}
		return results;
	}

	@Override
	public String getStringField(ObjectNode input, String field)
			throws CPSException {
		if (input.get(field) != null) {
			return input.get(field).asText();
		} else {
			return null;
		}
		// try {
		// Preconditions.checkNotNull(input.get(field));
		// return input.get(field).getAsString();
		// } catch (NullPointerException e) {
		// throw new CPSException(ErrorCode.KEY_NOT_EXIST_CODE,
		// "The value of " + field + " doesn't exist.");
		// }
	}

	@Override
	public String getNoExceptionStringField(ObjectNode input, String field)
			throws CPSException {
		try {
			Preconditions.checkNotNull(input.get(field));
			return input.get(field).asText();
		} catch (NullPointerException e) {
			return null;
		}
	}

	@Override
	public boolean isStringField(ObjectNode object, String fieldName)
			throws CPSException {
		checkNotNull(object, fieldName);
		try {
			if (object.get(fieldName).isArray()) {
				return false;
			}
			object.get(fieldName).asText();
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	@Override
	public boolean isIntField(ObjectNode object, String fieldName)
			throws CPSException {
		checkNotNull(object, fieldName);
		try {
			object.get(fieldName).asInt();
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	@Override
	public boolean isBooleanField(ObjectNode object, String fieldName)
			throws CPSException {
		checkNotNull(object, fieldName);
		try {
			object.get(fieldName).asBoolean();
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	@Override
	public boolean isArrayField(ObjectNode object, String fieldName)
			throws CPSException {
		checkNotNull(object, fieldName);
		if (!object.get(fieldName).isArray()) {
			try {
				String array = object.get(fieldName).asText();
				JsonNode element = JSONUtility.fromString(array);
				if (element.isArray()) {
					ArrayNode arrayNode = (ArrayNode) JSONUtility
							.fromString(array);
					object.set(fieldName, arrayNode);
				}
				// JsonElement value = parser.parse(array);
				// object.add(fieldName, value);
			} catch (Exception e) {
				return false;
			}
		}
		return object.get(fieldName).isArray();
	}

	private void checkNotNull(ObjectNode object, String fieldName)
			throws CPSException {
		if (object.get(fieldName) == null) {
			throw new CPSException(ErrorCode.NULL_FIELD_ERROR,
					"Please provide the value of " + fieldName + " in "
							+ object.toString());
		}
	}

	@Override
	public void setStringField(ObjectNode element, String field, String value) {
		element.put(field, value);
	}

	@Override
	public boolean hasStringField(ObjectNode input, String field)
			throws CPSException {
		if (getStringField(input, field) == null) {
			return false;
		} else {
			return true;
		}
	}

	@Override
	public int getIntField(ObjectNode input, String field) throws CPSException {
		if (input.get(field) == null) {
			throw new CPSException(ErrorCode.KEY_NOT_EXIST_CODE,
					"The value of " + field + " doesn't exist.");
		} else {
			return input.get(field).asInt();
		}
	}

	@Override
	public void setArrayField(ObjectNode input, String field, String... values) {
		ArrayNode arrayNode = JSONUtility.newArrayNode();
		for (String value : values) {
			arrayNode.add(value);
		}
		input.set(field, arrayNode);
	}

	@Override
	public void removeField(ObjectNode element, String field) {
		element.remove(field);
	}

	public static void main(String[] args) throws CPSException {
		JsonMessageParser parser = new JsonMessageParser();
		String parent = "parent";
		String string = "{\"parent\":\"a\"}";
		Collection<ObjectNode> output = parser.parse(string);
		for (ObjectNode object : output) {
			if (parser.isStringField(object, parent)) {
				String value = parser.getStringField(object, parent);
				parser.setArrayField(object, parent, value);
			}
			System.out.println(parser.isArrayField(object, parent));
		}
	}

	@Override
	public Collection<String> getArrayField(ObjectNode input, String field) {
		if (input.has(field) && input.get(field).isArray()) {
			JsonNode output = input.get(field);
			Iterator<JsonNode> ite = output.iterator();
			Collection<String> array = new ArrayList<String>();
			while (ite.hasNext()) {
				array.add(ite.next().toString());
			}
			return array;
		} else {
			return null;
		}
	}

}
